package io.gitee.mingbaobaba.security.plugin.redis.repository;

import io.gitee.mingbaobaba.security.core.constants.SecurityConstant;
import io.gitee.mingbaobaba.security.core.domain.SecuritySession;
import io.gitee.mingbaobaba.security.core.domain.SecurityToken;
import io.gitee.mingbaobaba.security.core.repository.SecurityRepository;
import io.gitee.mingbaobaba.security.core.utils.DateUtil;
import io.gitee.mingbaobaba.security.core.utils.JsonUtil;
import io.gitee.mingbaobaba.security.plugin.redis.constants.SecurityRedisConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>redis存储</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/8/29 10:09
 */
@Slf4j
public class SecurityRedisRepository implements SecurityRepository {

    private final StringRedisTemplate stringRedisTemplate;

    public SecurityRedisRepository(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 根据登录Id查询SecuritySession
     *
     * @param loginId 登录Id
     * @return SecuritySession
     */
    @Override
    public SecuritySession getSecuritySessionByLoginId(String loginId) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.SESSION_INFO_KEY, loginId);
        String elementStr = stringRedisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isBlank(elementStr)) {
            return null;
        }
        return JsonUtil.jsonStrToObject(elementStr, SecuritySession.class);
    }

    /**
     * 根据登录Id查询SecuritySession的过期时间
     *
     * @param loginId 登录Id
     * @return Long
     */
    @Override
    public Long getSessionTimeoutByLoginId(String loginId) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.SESSION_INFO_KEY, loginId);
        return stringRedisTemplate.getExpire(redisKey);
    }

    @Override
    public boolean saveSecuritySession(SecuritySession session) {
        String elementStr = JsonUtil.objectToJsonStr(session);
        String redisKey = MessageFormat.format(SecurityRedisConstant.SESSION_INFO_KEY, session.getLoginId());
        if (null == session.getTimeout() || SecurityConstant.NON_EXPIRING.equals(session.getTimeout())) {
            stringRedisTemplate.opsForValue().set(redisKey, elementStr);
        } else {
            //session为每个token共用，所以每次更新时超时时间都重置为配置的最大超时时间
            stringRedisTemplate.opsForValue().set(redisKey, elementStr, session.getTimeout(), TimeUnit.SECONDS);
        }
        return true;
    }

    /**
     * 根据登录Id删除SecuritySession
     *
     * @param loginId 登录Id
     * @return boolean
     */
    @Override
    public boolean removeSecuritySessionByLoginId(String loginId) {
        //删除session信息
        String redisKey = MessageFormat.format(SecurityRedisConstant.SESSION_INFO_KEY, loginId);
        return Boolean.TRUE.equals(stringRedisTemplate.delete(redisKey));
    }

    /**
     * 根据tokenValue获取SecurityToken
     *
     * @param tokenValue token
     * @return SecurityToken
     */
    @Override
    public SecurityToken getSecurityTokenByTokenValue(String tokenValue) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.TOKEN_REL_LOGIN_ID_KEY, tokenValue);
        String valueStr = stringRedisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isBlank(valueStr)) {
            return null;
        }
        SecurityToken securityToken = JsonUtil.jsonStrToObject(valueStr, SecurityToken.class);
        if (Objects.isNull(securityToken)) {
            return null;
        }
        return securityToken;
    }

    /**
     * 根据tokenValue获取活跃时间
     *
     * @param tokenValue token
     * @return String
     */
    @Override
    public String getActivityTimeByTokenValue(String tokenValue) {
        SecurityToken securityToken = getSecurityTokenByTokenValue(tokenValue);
        if (Objects.isNull(securityToken)) {
            return null;
        }
        return securityToken.getActivityTime();
    }

    /**
     * 根据tokenValue获取token活跃超时时间
     *
     * @param tokenValue token
     * @return Long
     */
    @Override
    public Long getTokenTimeOutByTokenValue(String tokenValue) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.TOKEN_REL_LOGIN_ID_KEY, tokenValue);
        return stringRedisTemplate.getExpire(redisKey);
    }

    /**
     * 根据tokenValue获取token活跃超时时间
     *
     * @param tokenValue token
     * @return Long
     */
    @Override
    public Long getTokenActivityTimeOutByTokenValue(String tokenValue) {
        SecurityToken securityToken = getSecurityTokenByTokenValue(tokenValue);
        if (Objects.isNull(securityToken)) {
            return null;
        }
        String activityTime = securityToken.getActivityTime();
        //计算超时时间
        long timeout = securityToken.getActivityTimeout();
        if (SecurityConstant.NON_EXPIRING.equals(timeout)) {
            return SecurityConstant.NON_EXPIRING;
        }
        //计算剩余时间
        long second = DateUtil.strToLocalDateTime.apply(activityTime).plusSeconds(timeout)
                .toEpochSecond(ZoneOffset.ofHours(8))
                - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
        return second > 0 ? second : 0L;
    }

    /**
     * 保存token信息
     *
     * @param token token信息
     * @return boolean
     */
    @Override
    public boolean saveToken(SecurityToken token) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.TOKEN_REL_LOGIN_ID_KEY, token.getToken());
        if (null == token.getTimeout() || SecurityConstant.NON_EXPIRING.equals(token.getTimeout())) {
            stringRedisTemplate.opsForValue().set(redisKey, JsonUtil.objectToJsonStr(token));
        } else {
            //获取超时时间
            Long timeout = stringRedisTemplate.getExpire(redisKey);
            if (Objects.isNull(timeout) || timeout <= 0) {
                timeout = token.getTimeout();
            }
            //重新保存token，超时时间需要延用创建token时的超时时间
            stringRedisTemplate.opsForValue().set(redisKey, JsonUtil.objectToJsonStr(token), timeout, TimeUnit.SECONDS);
        }
        return true;
    }

    /**
     * 根据tokenValue删除token信息
     *
     * @param tokenValue token
     * @return boolean
     */
    @Override
    public boolean removeTokenByTokenValue(String tokenValue) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.TOKEN_REL_LOGIN_ID_KEY, tokenValue);
        return Boolean.TRUE.equals(stringRedisTemplate.delete(redisKey));
    }

    /**
     * 根据token值续约
     *
     * @param tokenValue token值
     * @return boolean
     */
    @Override
    public boolean renewalTokenByTokenValue(String tokenValue) {
        SecurityToken securityToken = getSecurityTokenByTokenValue(tokenValue);
        if (Objects.isNull(securityToken)) {
            return false;
        }
        securityToken.setActivityTime(DateUtil.formatDateTime.apply(new Date()));
        return saveToken(securityToken);
    }

    /**
     * 查询所有token列表
     *
     * @param tokenValue token值，支持模糊匹配
     * @param sortedDesc 是否降序
     * @return List<String>
     */
    @Override
    public List<String> queryTokenList(String tokenValue, boolean sortedDesc) {
        String redisKey = MessageFormat.format(SecurityRedisConstant.TOKEN_REL_LOGIN_ID_KEY,
                StringUtils.isNoneBlank(tokenValue) ? tokenValue + "*" : "*");
        Set<String> setList = stringRedisTemplate.keys(redisKey);
        List<String> list = null == setList ? new ArrayList<>() : setList.stream().map(key ->
                        key.substring(key.lastIndexOf(":") + 1))
                .sorted(Comparator.comparing(String::toString)).collect(Collectors.toList());
        if (Boolean.TRUE.equals(sortedDesc)) {
            Collections.reverse(list);
        }
        return list;
    }
}
